﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace Reader.Libs.PRC.Metadata
{
    public class PDBHead
    {
        private byte[] name = new byte[32];

        private byte[] attributes = new byte[2];
        private byte[] version = new byte[2];
        private byte[] creationDate = new byte[4];
        private byte[] modificationDate = new byte[4];
        private byte[] lastBackupDate = new byte[4];
        private byte[] modificationNumber = new byte[4];
        private byte[] appInfoID = new byte[4];
        private byte[] sortInfoID = new byte[4];
        private byte[] type = new byte[4];
        private byte[] creator = new byte[4];
        private byte[] uniqueIDSeed = new byte[4];
        private byte[] nextRecordListID = new byte[4];
        private byte[] numRecords = new byte[2];
        public List<RecordInfo> recordInfoList = new List<RecordInfo>();
        private byte[] gapToData = new byte[2];

        public PDBHead()
        {
        }

        public void readPDBHead(FileStream fs)
        {
            fs.Read(this.name, 0, this.name.Length);
            fs.Read(this.attributes, 0, this.attributes.Length);
            fs.Read(this.version, 0, this.version.Length);
            fs.Read(this.creationDate, 0, this.creationDate.Length);
            fs.Read(this.modificationDate, 0, this.modificationDate.Length);
            fs.Read(this.lastBackupDate, 0, this.lastBackupDate.Length);
            fs.Read(this.modificationNumber, 0, this.modificationNumber.Length);
            fs.Read(this.appInfoID, 0, this.appInfoID.Length);
            fs.Read(this.sortInfoID, 0, this.sortInfoID.Length);

            fs.Read(this.type, 0, this.type.Length);
            fs.Read(this.creator, 0, this.creator.Length);
            fs.Read(this.uniqueIDSeed, 0, this.uniqueIDSeed.Length);
            fs.Read(this.nextRecordListID, 0, this.nextRecordListID.Length);
            fs.Read(this.numRecords, 0, this.numRecords.Length);

            int recordCount = Converter.ToInt16(this.numRecords);

            for (int i = 0; i < recordCount; i++)
            {
                RecordInfo ri = new RecordInfo();
                ri.readRecordInfo(fs);
                this.recordInfoList.Add(ri);
            }

            fs.Read(this.gapToData, 0, this.gapToData.Length);
        }

        public void writePDBHead(FileStream fs, int re_Count)
        {
            try
            {
                byte[] _name = System.Text.Encoding.ASCII.GetBytes("Sample");
                for (int i = 0; i < _name.Length && i < 32; i++)
                {
                    name[i] = _name[i];
                }

                TimeSpan ts = DateTime.Now - DateTime.Parse("01-01-1904");
                this.creationDate = BitConverter.GetBytes((uint)ts.TotalSeconds);
                

                this.type = new byte[4] { 66, 79, 79, 75 };
                this.creator = new byte[4] { 77, 79, 66, 73 };

                this.numRecords = BitConverter.GetBytes((ushort)(re_Count + 2));

                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(creationDate);
                    Array.Reverse(numRecords);
                }

                this.modificationDate = this.creationDate;
                this.lastBackupDate = this.creationDate;


                fs.Write(this.name, 0, this.name.Length);
                fs.Write(this.attributes, 0, this.attributes.Length);
                fs.Write(this.version, 0, this.version.Length);
                fs.Write(this.creationDate, 0, this.creationDate.Length);
                fs.Write(this.modificationDate, 0, this.modificationDate.Length);
                fs.Write(this.lastBackupDate, 0, this.lastBackupDate.Length);
                fs.Write(this.modificationNumber, 0, this.modificationNumber.Length);
                fs.Write(this.appInfoID, 0, this.appInfoID.Length);
                fs.Write(this.sortInfoID, 0, this.sortInfoID.Length);

                fs.Write(this.type, 0, this.type.Length);
                fs.Write(this.creator, 0, this.creator.Length);
                fs.Write(this.uniqueIDSeed, 0, this.uniqueIDSeed.Length);
                fs.Write(this.nextRecordListID, 0, this.nextRecordListID.Length);
                fs.Write(this.numRecords, 0, this.numRecords.Length);

                UInt32 index = 78;
                index += (UInt32)(NumRecords * 8) + 2;
                for (int i = 0; i < NumRecords; i++)
                {
                    RecordInfo ri = new RecordInfo();
                    ri.RecordDataOffset = index;
                    ri.writeRecordInfo(fs);
                    this.recordInfoList.Add(ri);
                    index += 4096;
                }

                fs.Write(this.gapToData, 0, this.gapToData.Length);
            }
            catch (Exception)
            {
                throw;
            }
            
        }

        public string Name
        {
            get { return Encoding.ASCII.GetString(this.name).Replace("\0", String.Empty); }
        }

        public ushort Attributes
        {
            get { return Converter.ToUInt16(this.attributes); }
        }

        public ushort Version
        {
            get { return Converter.ToUInt16(this.version); }
        }

        public uint CreationDate
        {
            get { return Converter.ToUInt32(this.creationDate); }
        }

        public uint ModificationDate
        {
            get { return Converter.ToUInt32(this.modificationDate); }
        }

        public uint LastBackupDate
        {
            get { return Converter.ToUInt32(this.lastBackupDate); }
        }

        public uint ModificationNumber
        {
            get { return Converter.ToUInt32(this.modificationNumber); }
        }

        public uint AppInfoID
        {
            get { return Converter.ToUInt32(this.appInfoID); }
        }

        public uint SortInfoID
        {
            get { return Converter.ToUInt32(this.sortInfoID); }
        }

        public string Type
        {
            get { return Encoding.ASCII.GetString(this.type);}
        }

        public string Creator
        {
            get { return Encoding.ASCII.GetString(this.creator); }
        }

        public uint UniqueIDSeed
        {
            get { return Converter.ToUInt32(this.uniqueIDSeed); }
        }

        public ushort NumRecords
        {
            get { return Converter.ToUInt16(this.numRecords); }
        }

        public ushort GapToData
        {
            get { return Converter.ToUInt16(this.gapToData); }
        }

        public uint MobiHeaderSize
        {
            get
            {
                if (this.recordInfoList.Count > 1)
                {
                    return ((RecordInfo)this.recordInfoList[1]).RecordDataOffset - ((RecordInfo)this.recordInfoList[0]).RecordDataOffset;
                }
                else
                {
                    return 0;
                }
            }
        }

        public class RecordInfo
        {
            private byte[] recordDataOffset = new byte[4];
            private byte recordAttributes = 0;
            private byte[] uniqueID = new byte[3];
            
            public RecordInfo(){ }

            public void readRecordInfo(FileStream fs)
            {
                fs.Read(this.recordDataOffset, 0, this.recordDataOffset.Length);
                recordAttributes = (byte)fs.ReadByte();
                fs.Read(this.uniqueID, 0, this.uniqueID.Length);
            }

            public void writeRecordInfo(FileStream fs)
            {
                fs.Write(this.recordDataOffset, 0, this.recordDataOffset.Length);
                fs.WriteByte(recordAttributes);
                fs.Write(this.uniqueID, 0, this.uniqueID.Length);
            }

            public uint RecordDataOffset
            {
                get { return Converter.ToUInt32(this.recordDataOffset); }
                set
                {
                    this.recordDataOffset = BitConverter.GetBytes(value);
                    if (BitConverter.IsLittleEndian)
                        Array.Reverse(recordDataOffset);
                }
            }
        }
    }
}
